package tools.jackson.databind.deser.creators;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

import tools.jackson.databind.*;
import tools.jackson.databind.cfg.MapperConfig;
import tools.jackson.databind.introspect.AnnotatedMember;
import tools.jackson.databind.introspect.AnnotatedParameter;
import tools.jackson.databind.introspect.JacksonAnnotationIntrospector;

import static org.junit.jupiter.api.Assertions.assertEquals;

import static tools.jackson.databind.testutil.DatabindTestUtil.a2q;
import static tools.jackson.databind.testutil.DatabindTestUtil.jsonMapperBuilder;
import static tools.jackson.databind.testutil.DatabindTestUtil.sharedMapper;

public class ImplicitNameMatch792Test
{
    // Simple introspector that gives generated "ctorN" names for constructor
    // parameters
    static class ConstructorNameAI extends JacksonAnnotationIntrospector
    {
        private static final long serialVersionUID = 1L;

        @Override
        public String findImplicitPropertyName(MapperConfig<?> config, AnnotatedMember member) {
            if (member instanceof AnnotatedParameter ap) {
                return String.format("ctor%d", ap.getIndex());
            }
            return super.findImplicitPropertyName(config, member);
        }
    }

    @JsonPropertyOrder({ "first" ,"second", "other" })
    static class Issue792Bean
    {
        String value;

        public Issue792Bean(@JsonProperty("first") String a,
                @JsonProperty("second") String b) {
            value = a;
            // ignore second arg
        }

        public String getCtor0() { return value; }

        public int getOther() { return 3; }
    }

    static class Bean2
    {
        int x = 3;

        @JsonProperty("stuff")
        private void setValue(int i) { x = i; }

        public int getValue() { return x; }
    }

    // 17-May-2024, tatu: [databind#4515] This is not a valid test; commenting
    //    out; to be removed in near future (after 2.18)
    /*
    static class ReadWriteBean
    {
        private int value;

        // 22-Sep-2017, tatu: Note that must be either `public`; annotated with JsonCreator,
        //    or visibility min level for creator auto-detection needs to be raised
        public ReadWriteBean(@JsonProperty(value="value",
                access=JsonProperty.Access.READ_WRITE) int v) {
            value = v;
        }

        @JsonProperty("value")
        public int testValue() { return value; }

        // Let's also add setter to ensure conflict resolution works
        public void setValue(int v) {
            throw new RuntimeException("Should have used constructor for 'value' not setter");
        }
    }
    */

    // Bean that should only serialize 'value', but deserialize both
    static class PasswordBean
    {
        @JsonProperty(access=JsonProperty.Access.WRITE_ONLY)
        private String password;

        private int value;

        public int getValue() { return value; }
        public String getPassword() { return password; }

        public String asString() {
            return String.format("[password='%s',value=%d]", password, value);
        }
    }

    /*
    /**********************************************************
    /* Test methods
    /**********************************************************
     */

    private final ObjectMapper MAPPER = sharedMapper();

    @Test
    public void testBindingOfImplicitCreatorNames() throws Exception
    {
        ObjectMapper m = jsonMapperBuilder()
                .annotationIntrospector(new ConstructorNameAI())
                .build();
        String json = m.writeValueAsString(new Issue792Bean("a", "b"));
        assertEquals(a2q("{'first':'a','other':3}"), json);
    }

    @Test
    public void testImplicitWithSetterGetter() throws Exception
    {
        String json = MAPPER.writeValueAsString(new Bean2());
        assertEquals(a2q("{'stuff':3}"), json);
    }

    // 17-May-2024, tatu: [databind#4515] This is not a valid test; commenting
    //    out; to be removed in near future (after 2.18)
    // 30-May-2024, tatu: Hmmh. Actually passes if commented out... should reconsider?
    /*
    @Test
    public void testReadWriteWithPrivateField() throws Exception
    {
        String json = MAPPER.writeValueAsString(new ReadWriteBean(3));
        assertEquals("{\"value\":3}", json);
    }
    */

    @Test
    public void testWriteOnly() throws Exception
    {
        PasswordBean bean = MAPPER.readValue(a2q("{'value':7,'password':'foo'}"),
                PasswordBean.class);
        assertEquals("[password='foo',value=7]", bean.asString());
        String json = MAPPER.writeValueAsString(bean);
        assertEquals("{\"value\":7}", json);
    }
}
